/******************************************************************************
 * Filename : uc_thread.c
 * Copyright:
 * Created  : 2011-1-6 by franco.yin
 * Description -
 *
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

#include "uc_thread.h"

//#define DEBUG_LOCK 1

void
uclock_init(uclock_s *pLock)
{
	pthread_mutex_init(&(pLock->lock), NULL);
}

void
uclock_init_recursive(uclock_s *pLock)
{
	pthread_mutexattr_t attr;

	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
	pthread_mutex_init(&(pLock->lock), &attr);
}

void
_uclock_lock(uclock_s *pLock, const char *func, int line)
{
#ifdef DEBUG_LOCK
	uctime_s t1;
	char old_func[UC_BUF_LEN];
	int old_line = 0;
	UC_U32 old_tid = 0;

	uctime_get(&t1);
	uc_strncpy(old_func, pLock->func, sizeof(old_func));
	old_line = pLock->line;
	old_tid = pLock->tid;
#endif

	pthread_mutex_lock(&(pLock->lock));

#ifdef DEBUG_LOCK
	uc_strncpy(pLock->func, func, sizeof(pLock->func));
	pLock->line = line;
	pLock->tid = uc_gettid();
	uctime_get(&(pLock->lock_time));
	pLock->take_cost = uctime_get_diff(&t1, &(pLock->lock_time));
	if(pLock->take_cost > 3) {
		uc_p("==> [%ld] lock[%s:%d] take cost[%d] old_owner[%s:%d-%d]\n",
			uc_gettid(), pLock->func, pLock->line, pLock->take_cost, old_func, old_line, old_tid);
	}
#endif

}

int
_uclock_trylock(uclock_s *pLock, const char *func, int line)
{
	int ret = 0;

#ifdef DEBUG_LOCK
	uctime_s t1;
	char old_func[UC_BUF_LEN];
	int old_line = 0;
	UC_U32 old_tid = 0;

	uctime_get(&t1);
	uc_strncpy(old_func, pLock->func, sizeof(old_func));
	old_line = pLock->line;
	old_tid = pLock->tid;
#endif

	ret = pthread_mutex_trylock(&((pLock)->lock));
	if(ret == 0) {
#ifdef DEBUG_LOCK
		uc_strncpy(pLock->func, func, sizeof(pLock->func));
		pLock->line = line;
		pLock->tid = uc_gettid();
		uctime_get(&(pLock->lock_time));
		pLock->take_cost = uctime_get_diff(&t1, &(pLock->lock_time));
		if(pLock->take_cost > 3) {
			uc_p("==> [%ld] lock[%s:%d] try take cost[%d] old_owner[%s:%d-%d]\n",
				uc_gettid(), pLock->func, pLock->line, pLock->take_cost, old_func, old_line, old_tid);
		}
#endif
	}

	return ret;
}

int
_uclock_unlock(uclock_s *pLock, const char *func, int line)
{
	int use_cost = 0;

#ifdef DEBUG_LOCK
	uctime_s t1;
	int take_cost = 0;
	char old_func[UC_BUF_LEN];
	int old_line = 0;
	UC_U32 old_tid = 0;

	uctime_get(&t1);
	pLock->use_cost = uctime_get_diff(&(pLock->lock_time), &t1);
	use_cost = pLock->use_cost;
	take_cost = pLock->take_cost;
	uc_strncpy(old_func, pLock->func, sizeof(old_func));
	old_line = pLock->line;
	old_tid = pLock->tid;
#endif

	pthread_mutex_unlock(&(pLock->lock));

#ifdef DEBUG_LOCK
	if(use_cost > 3) {
		uc_p("===================================> [%ld] lock[%s:%d-%d] unlock[%s:%d] cost[%d-%d]\n", uc_gettid(),
			old_func, old_line, old_tid, func, line, take_cost, use_cost);
	}
#endif

	return use_cost;
}

int
ucrwlock_init(ucrwlock_s *rwLock)
{
	pthread_rwlockattr_t rwAttr;

	pthread_rwlockattr_init(&rwAttr);
	return pthread_rwlock_init(rwLock, &rwAttr);
}

int
ucrwlock_rlock(ucrwlock_s *rwLock)
{
	return pthread_rwlock_rdlock(rwLock);
}

int
ucrwlock_wlock(ucrwlock_s *rwLock)
{
	return pthread_rwlock_wrlock(rwLock);
}

int
ucrwlock_unlock(ucrwlock_s *rwLock)
{
	return pthread_rwlock_unlock(rwLock);
}

int
ucrwlock_destroy(ucrwlock_s *rwLock)
{
	return pthread_rwlock_destroy(rwLock);
}

void
ucsig_init(ucsig_s *pSig)
{
	pthread_cond_init(&(pSig->cond), NULL);
	pthread_mutex_init(&(pSig->mutex), NULL);
}

void
ucsig_wait(ucsig_s *pSig)
{
	pthread_mutex_lock(&(pSig->mutex));
	pthread_cond_wait(&(pSig->cond), &(pSig->mutex));
	pthread_mutex_unlock(&(pSig->mutex));
}

int
ucsig_wait_timeout(ucsig_s *pSig, int msec)
{
	struct timespec ts;
	uctime_s now_time;
	int ret = 0;

	uctime_get(&now_time);
	ts.tv_sec = now_time.tv_sec + msec / 1000;
	ts.tv_nsec = now_time.tv_usec * 1000 + (msec % 1000) * 1000000;
	if (ts.tv_nsec >= 1000000000) {
		ts.tv_sec += 1;
		ts.tv_nsec -= 1000000000;
	}

	pthread_mutex_lock(&(pSig->mutex));
	if(msec > 0) {
		ret = pthread_cond_timedwait(&(pSig->cond), &(pSig->mutex), (const struct timespec *)&ts);
	} else {
		ret = pthread_cond_wait(&(pSig->cond), &(pSig->mutex));
	}
	pthread_mutex_unlock(&(pSig->mutex));

	return ret;
}

void
ucsig_destroy(ucsig_s *pSig)
{
	pthread_cond_destroy(&(pSig->cond));
	pthread_mutex_destroy(&(pSig->mutex));
}

/***************************************************************
 * Function: ucthread_attr_init
 * Created : 2011-9-20  by franco.yin
 * Description -
 *  schedul: SCHED_FIFO  SCHED_RR  SCHED_OTHER
 *  priority: 1 - 99 (only in FIFO or RR mode)
 */
int
ucthread_attr_init(ucthread_attr_s *pAttr, int if_detached, int stack_size, int if_inherit, int schedul, int priority)
{
	struct sched_param param;

	pthread_attr_init(pAttr);
	if(if_detached > 0) {
		pthread_attr_setdetachstate(pAttr, PTHREAD_CREATE_DETACHED);
	}
	pthread_attr_setstacksize(pAttr, stack_size);
	if(if_inherit <= 0) {
		pthread_attr_setinheritsched(pAttr, PTHREAD_EXPLICIT_SCHED);

		pthread_attr_setschedpolicy(pAttr, schedul);

		if(schedul == SCHED_FIFO || schedul == SCHED_RR) {
			param.sched_priority = priority;
			pthread_attr_setschedparam(pAttr, &param);
		}
	} else {
		pthread_attr_setinheritsched(pAttr, PTHREAD_INHERIT_SCHED);
	}

/*
{
	int inherit = 0;
	int detachstate = 0;
	int policy = 0;

	pthread_attr_getinheritsched(pAttr, &inherit);
	pthread_attr_getdetachstate(pAttr, &detachstate);
	pthread_attr_getschedpolicy(pAttr,&policy);
	pthread_attr_getschedparam(pAttr,&param);

	uc_p("inherit[%d] detachstate[%d] policy[%d] priority[%d]\n", inherit, detachstate, policy, param.sched_priority);
}
*/
	return OK;
}

static void
ucthread_job_exit(void *p)
{
	ucthread_s *pThread = (ucthread_s *)p;

	if(p == NULL) {
		return;
	}

	if(pThread->do_job_exit != NULL) {
		pThread->do_job_exit(pThread->param);
	}

//	ucsig_destroy(&(pThread->sig));

	uc_info("--> thread end[%s][%s:%s:%d] tid[%ld] <--\n", pThread->name, pThread->file, pThread->func, pThread->line, uc_gettid());
	return;
}

void *
ucthread_job(void *p)
{
	int oldstate = 0;
	int oldtype = 0;
	ucthread_s *pThread = (ucthread_s *)p;
	char schedul_name[UC_BUF_LEN];
	struct sched_param param;
	int schedul = 0;
	int priority = 0;

	pthread_getschedparam(pthread_self(), &schedul, &param);
	priority = param.__sched_priority;
	switch (schedul) {
		case SCHED_FIFO:
		uc_strncpy(schedul_name, "FIFO", sizeof(schedul_name));
		break;
		case SCHED_RR:
			uc_strncpy(schedul_name, "RR", sizeof(schedul_name));
		break;
		case SCHED_OTHER:
			uc_strncpy(schedul_name, "OTHER", sizeof(schedul_name));
		break;
		default:
			uc_strncpy(schedul_name, "UNKNOWN", sizeof(schedul_name));
		break;
	}

	uc_info("++> thread start[%s][%s:%s:%d] tid[%ld] schedul[%s] priority[%d] <++\n",
		pThread->name, pThread->file, pThread->func, pThread->line, uc_gettid(),
		schedul_name, priority);

	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);

	pthread_cleanup_push(ucthread_job_exit, p);

	uclock_lock(&(pThread->lock_thread));
	pThread->if_run = UCTHREAD_RUNNING;
	uclock_unlock(&(pThread->lock_thread));

	pThread->do_job(pThread->param);

	uclock_lock(&(pThread->lock_thread));
	pThread->if_run = UCTHREAD_RELEASE;
	uclock_unlock(&(pThread->lock_thread));

	pthread_cleanup_pop(1);

	uclock_lock(&(pThread->lock_thread));
	pThread->if_run	= UCTHREAD_IDLE;
	uclock_unlock(&(pThread->lock_thread));

	return NULL;
}

int
_ucthread_init(ucthread_s *pThread, char *name, ucthread_attr_s *pAttr,
	ucthread_func do_func, ucthread_func_exit exit_func, void *param, char *file, const char *func, int line)
{
	if(do_func == NULL || pThread == NULL) {
		return ERROR;
	}

	uclock_init_recursive(&(pThread->lock_thread));

	pThread->if_run = UCTHREAD_IDLE;

	ucsig_init(&(pThread->sig));

	uc_strncpy(pThread->name, name, sizeof(pThread->name));
	if(pAttr == NULL) {
		ucthread_attr_init(&pThread->attr, 1, UC_DEFAULT_STACK_SIZE, 1, 0, 0);
	} else {
		memcpy(&pThread->attr, pAttr, sizeof(ucthread_attr_s));
	}

	uc_strncpy(pThread->file, file, sizeof(pThread->file));
	uc_strncpy(pThread->func, func, sizeof(pThread->func));
	pThread->line = line;

	pThread->do_job = do_func;
	pThread->do_job_exit = exit_func;
	pThread->param = param;

	return OK;
}

int
ucthread_run(ucthread_s *pThread)
{
	if(pThread == NULL || pThread->do_job == NULL) {
		return ERROR;
	}

	uclock_lock(&(pThread->lock_thread));
	if(pThread->if_run > UCTHREAD_IDLE) {
		uclock_unlock(&(pThread->lock_thread));
		uc_info("ucthread_run thread[%s] already running\n", pThread->name);
		return OK;
	}

	if (pthread_create(&(pThread->id), &pThread->attr, ucthread_job, (void *)(pThread)) != 0) {
		uclock_unlock(&(pThread->lock_thread));
		uc_err("ucthread_run thread[%s] error\n", pThread->name);
		return ERROR;
	}

	pThread->if_run = UCTHREAD_INIT;
	uclock_unlock(&(pThread->lock_thread));
	return OK;
}

int
ucthread_status(ucthread_s *pThread)
{
	int ret = 0;

	uclock_lock(&(pThread->lock_thread));
	ret = pThread->if_run;
	uclock_unlock(&(pThread->lock_thread));

	return ret;
}

pthread_t
ucthread_create_simple(ucthread_func do_func, void *param)
{
	ucthread_attr_s attr;
	pthread_t thread;

	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
	pthread_create(&thread, &attr, do_func, param);
	pthread_attr_destroy(&attr);

	return thread;
}

#ifdef __cplusplus
}
#endif
